﻿/// <reference path="../jquery-1.4.4.js" />
/// <reference path="../jquery-1.4.4-vsdoc.js" />

/*!  
** Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
** Dual licensed under the MIT and GPL licenses:
** http://www.opensource.org/licenses/mit-license.php
** http://www.gnu.org/licenses/gpl.html
** 
**----------------------------------------------------------------
** title        : DJME Combo
** version   : 2.0.3
** modified: 2011-2-23
** depends:
**    jquery.ui.core.js
**    jquery.ui.widget.js
**    jquery.tmpl.js
**----------------------------------------------------------------
*/

(function ($) {
    $.widget("ui.combo", {
        options: {
            dropdownStyle: "dropdown", //dropdown dropdownlist
            filterMode: false,
            source: null,
            selectedValue: null,
            selectedItem: null,
            dataTextField: "span",
            dataValueField: "input[type='hidden']",
            value: null,
            text: null,
            selected: null,
            cssClass: null,
            hoverClass: null,
            itemClass: null,   // "ui-widget-content",
            itemHoverClass: null, // "ui-state-hover",
            itemSelectedClass: null, // "ui-state-highlight"
            itemTemplate: null,
            particalLoad: false,
            autofit: false
        },
        _create: function () {
            var self = this, opts = this.options, eventPrefix = this.widgetEventPrefix, el = this.element;
            this._prepareUI();
            _hoverHandler = function (event) {
                if (opts.hoverClass)
                    self.container.toggleClass(opts.hoverClass);
                self.container.toggleClass("d-combo-hover");
            };

            this.dropdownButton = $("<span />").addClass("d-combo-button")
                                                                      .appendTo(this.container)
                                                                      .bind("mouseenter", _hoverHandler)
                                                                      .bind("mouseleave", _hoverHandler)
                                                                      .bind("click", self._delegate(self, self._onclick));
            $(document).bind("click", self._delegate(self, self.deactive));

            if (opts.selected)
                el.bind(eventPrefix + "selected", opts.selected);

            this.textField.bind("mouseenter", _hoverHandler)
                            .bind("mouseleave", _hoverHandler)
                            .bind("click", self._delegate(self, self._onclick))

            if (this.options.dropdownStyle == "dropdownlist") {
                el.parent().addClass("d-combo-dropdown");
                this.textField.disableSelection();
            }
            else {
                if (this.options.filterMode)
                    this.textField.bind("keyup", self._delegate(self, self._onfilter));
            }

            if (opts.selectedValue) {
                this._setOption("selectedValue", opts.selectedValue);
            }
        },
        _setOption: function (key, value) {
            if (key == "selectedValue") {
                var self = this;
                if (!this.loaded) {
                    this.dataBind(function () {
                        self.loaded = true;
                        //Here maybe has bug.
                    });
                }
                var _input = this.panel.children(".d-combo-item-container").find("input[value='" + value + "']");
                if (_input.length)
                    this.select(null, $(_input).closest("li"));
            }
        },
        _onfilter: function (e, element) {
            if (this.element.val()) {
                var items = this.panel.find(".d-combo-item:not(:contains('" + this.element.val() + "'))");
                if (items.length) items.hide();
            }
            else
                this.panel.find(".d-combo-item").show();
            this.active(e);
        },
        _delegate: function (context, handler) {
            return function (e) {
                handler.apply(context, [e, this]);
            };
        },
        _onclick: function (event) {
            event.stopPropagation();
            if (this.element.parent().hasClass("d-combo-open"))
                this.deactive(event);
            else
                this.active(event);
        },
        search: function (term) {
        },
        setValue: function (txt, _val) {
            this.textField.val(txt);
            if (_val) this.element.val(_val);
            else
                this.element.val(txt);
            this.deactive();
        },
        select: function (event, element) {
            if (element) {
                /*------------------begin set value and text------------------------------*/
                var txtField = this.options.dataTextField ? this.options.dataTextField : "input:first",
                valField = this.options.dataValueField ? this.options.dataValueField : "input:first",
                $txt = $(element).find(txtField),
                $val = $(element).find(valField);

                if ($txt.length) {
                    if ($txt.get(0).tagName == "INPUT")
                        this.textField.val($txt.val())
                    else
                        this.textField.val($txt.text());
                };

                if ($val.length) {
                    var _v = "";
                    if ($val.get(0).tagName == "INPUT")
                        _v = $val.val(); else
                        _v = $val.text();
                    this.element.val(_v);
                    this.options.selectedValue = _v;
                    if ($txt.length == 0) this.textField.val(_v);
                }
                /*------------------end set value and text-------------------------------*/
                var $items = $(element).siblings(".d-combo-item-selected");
                $items.removeClass("d-combo-item-selected");
                if (this.options.itemSelectedClass) {
                    $items.removeClass(this.options.itemSelectedClass);
                    $(element).addClass(this.options.itemSelectedClass);
                }
                $(element).addClass("d-combo-item-selected");
                this.options.selectedItem = element;
                //this.element.trigger(this.widgetEventPrefix + "selected", element);
            }
            this.deactive(event);
        },
        active: function (event) {
            var self = this;
            if (!this.loaded) {
                this.dataBind(function () {
                    self.loaded = true;
                    self._play();
                });
                return;
            }
            this._play();
        },
        _play: function () {
            var others = $("body").find(".d-combo-open");
            if (others.length) {
                others.each(function (i, n) {
                    $(n).children("input").combo("deactive");
                });
            }
            this.element.parent().addClass("d-combo-open");
            this.panel.show("slide", { direction: "up" }, 100);
        },
        deactive: function (event) {
            if (this.element.parent().hasClass("d-combo-open")) {
                this.panel.hide("slide", { direction: "up" }, 100);
                this.element.parent().removeClass("d-combo-open");
                var _hs = this.element.find(".d-combo-item-hover");
                if (_hs.length) _hs.removeClass("d-combo-item-hover");
            }
        },
        _contains: function (parent, child) {
            try {
                return $.contains(parent, child);
            } catch (e) {
                return false;
            }
        },
        dataBind: function (fn) {
            var src = this.options.source;
            if (src) {
                if ($.type(src) == "string")
                    return this.bindToRemote(src, fn);
                if ($.isArray(src))
                    this.bindToArray(src);
                if (src.jquery)
                    this.bindToElement(src);
                if ($.isFunction(fn)) fn();
            }
        },
        bindToArray: function (args) {
            var $ul = $("<ul />").appendTo("body");       //.addClass("d-combo-item-container")

            $.each(args, function (i, n) {
                var $li = $("<li />").appendTo($ul);
                if ($.type(n) == "string") {
                    $("<a href='#'>" + n + "<input type='hidden' value='" + n + "'></a>").appendTo($li);
                }
                else {
                    if (n.selected)
                        $li.addClass("d-combo-item-selected");
                    $("<a href='#'>" + n.label + "<input type='hidden' value='" + n.value + "'></a>").appendTo($li);
                }
            });
            this.bindToElement($ul);
        },
        _particalLoad: function () {
            var self = this;
            if (this._routeValues) {
                var _rvalues = self._routeValues;
                if (_rvalues.total >= _rvalues.index) {
                    var $ul = $(".d-combo-item-container", self.element.parent());
                    $.ajax({
                        type: "POST",
                        data: { size: _rvalues.size, index: _rvalues.index },
                        url: this.options.source,
                        dataType: "json",
                        beforeSend: function () {
                            self._createLoader();
                        },
                        success: function (data) {
                            $("#" + self.options.itemTemplate).tmpl(data.Model).appendTo($ul);
                            self._initItems($ul.find("li:not(.d-combo-item)"));
                            self._routeValues.index++;
                            self._removeLoader();
                        }
                    });
                }
            }
        },
        bindToRemote: function (uri, fn) {
            var self = this;
            this._routeValues = null;

            if (self.options.particalLoad) {
                this._routeValues = {
                    size: 20,
                    index: 1
                };
            }

            $.ajax({
                url: uri,
                type: "POST",
                data: this._routeValues,
                dataType: "json",
                beforeSend: function () {
                    self.dropdownButton.addClass("d-combo-load");
                },
                success: function (data) {
                    if ((self.options.itemTemplate) && ($.fn.tmpl)) {
                        var $ul = $("<ul />").appendTo("body");

                        $("#" + self.options.itemTemplate).tmpl(data.Model).appendTo($ul);
                        self._routeValues.count = data.Total;
                        self._routeValues.total = Math.round(data.Total / self._routeValues.size);
                        self._routeValues.index++;
                        self.bindToElement($ul);
                        self.dropdownButton.removeClass("d-combo-load");
                        var panel = $(".d-combo-panel", self.element.parent());
                        panel.bind("scroll", function (event) {
                            var st = panel.scrollTop(),
                            ph = panel.height(),
                            ch = $ul.height();

                            if ((ch - st) <= ph) {
                                self._particalLoad();
                            }
                        });
                    }
                    else
                        self.bindToArray(data);
                    if ($.isFunction(fn)) fn();
                }
            });
        },
        _removeLoader: function () {
            var loader = $(".d-loader", this.element.parent());
            if (loader.length) loader.remove();
        },
        _createLoader: function () {
            var _loader = $("<div/>").addClass("d-loader d-combo-load").appendTo(this.element.parent());
            _loader.css({
                height: this.panel.height() + "px",
                width: this.panel.width() + "px",
                "left": "0px",
                "top": this.panel.position().top + "px"
            });
            return _loader;
        },
        bindToCallback: function (callback, fn) {
            var self = this;
            if ($.isFunction(callback)) {
                callback(function (items) {
                    self.bindToArray(items);
                    if ($.isFunction(fn)) fn();
                });
            }
        },
        _initItems: function ($items) {
            var self = this;
            $items.addClass("d-combo-item" + (self.options.itemClass ? " " + self.options.itemClass : ""))

                              .disableSelection();

            $items.each(function (i, n) {
                $(n).bind("mouseenter", function (event) {
                    event.stopPropagation();
                    $(this).addClass("d-combo-item-hover");
                    if (self.options.itemHoverClass)
                        $(this).addClass(self.options.itemHoverClass);
                })
                                .bind("mouseleave", function (event) {
                                    event.stopPropagation();
                                    $(this).removeClass("d-combo-item-hover");
                                    if (self.options.itemHoverClass)
                                        $(this).removeClass(self.options.itemHoverClass);
                                })
                                 .click(function (event) {
                                     event.stopPropagation();
                                     if (self.options.itemHoverClass)
                                         $items.removeClass(self.options.itemHoverClass);
                                     self.select(event, this);

                                     self.element.trigger(self.widgetEventPrefix + "selected", { item: $(this), value: self.options.selectedValue });
                                     //self.element.trigger(self.widgetEventPrefix + "selected", {sender:this,});
                                 });
            });
        },
        bindToElement: function (element) {
            if ((element) && (this.panel)) {
                //if (!this._contains(this.panel, element)) {
                if (!element.hasClass("d-combo-item-container")) {
                    var self = this;
                    this.panel.append(element);

                    element.addClass("d-combo-item-container")
                                .css("display", "block").disableSelection();
                    var $items = element.children("li");
                    self._initItems($items);
                }
            }
        },

        _prepareUI: function () {
            var element = this.element;
            if (!element.parent().hasClass("d-combo")) {
                element.wrap(
                             $('<div/>').addClass('d-combo')
                //.css({
                // width: (element.outerWidth(true) + 20) + "px"
                //})
                             );
                this.container = element.parent();

                if (this.options.autofit) {
                    //this.container.width(this.container.parent().innerWidth(true));
                    this.container.css({ width: "auto", display: "block" });
                    this.element.width(this.container.width() - 24);
                }
                else {
                    if (element.width()) {
                        //this.container.width(element.width());
                        this.element.width(element.width() - 20);
                    }
                }


                this.textField = $("<input />").addClass("d-combo-input")
                                                             .prependTo(this.container);
                if (element.attr("style"))
                    this.textField.attr("style", element.attr("style"));
                element.hide();

                if (this.options.text) this.textField.val(this.options.text);
                if (this.options.value) this.element.val(this.options.value);


                //prepare dropdownPanel
                var _pane = $("<div/>").addClass("d-combo-panel")
                               .css({
                                   "min-width": this.container.width() + "px",
                                   "position": "absolute",
                                   "left": "0px",
                                   "top": this.container.outerHeight(true) ? (this.container.outerHeight(true) + "px") : "23px"
                               })
                               .appendTo(this.container);

                this.panel = _pane;
                _pane.hide().disableSelection();
            }
        },
        addItem: function (_text, _value) {
            var item = $("<li/>"),
            _valEl = $("<input type='hidden' />").val(_value),
            _txtEl = $("<span/>").text(_text),
            $ul = $(".d-combo-item-container", self.element.parent());

            item.append(_valEl)
                   .append(_txtEl);
            $ul.append(item);
        },
        destroy: function () {
            $.Widget.prototype.destroy.call(this);
        }
    });
})(jQuery);   